// 导入TensorFlow.js库，这是整个机器学习的核心
import * as tf from '@tensorflow/tfjs';
// 导入可视化库，用来画图展示数据和训练过程
import * as tfvis from '@tensorflow/tfjs-vis';
// 导入我们的数据生成函数，从data.js文件中
import { getData } from './data.js';

// 页面加载完成后，异步执行所有代码
window.onload = async () => {
  // 调用getData函数生成400个数据点，这些点会被分成两个类别
  const data = getData(400);
  // 在控制台打印数据，方便我们查看生成的数据长什么样
  console.log(data)
  
  // 使用tfvis库渲染一个散点图，展示我们的训练数据
  tfvis.render.scatterplot(
    { name: '训练数据' },  // 图表的名称
    {
      // 把数据按照标签分成两组，分别显示
      values: [
        data.filter(p => p.label === 0),  // 标签为0的数据点
        data.filter(p => p.label === 1)   // 标签为1的数据点
      ]
    }
  )
  
  // 创建一个顺序模型，这是一个最基础的神经网络模型结构
  const model = tf.sequential()
  
  // 向模型中添加一个全连接层（也叫密集层）
  model.add(tf.layers.dense({
    units: 1,               // 输出单元的数量，这里是1个（二分类问题）
    inputShape: [2],        // 输入数据的形状，这里是2维数据（x和y坐标）
    activation: 'sigmoid'   // 激活函数使用sigmoid，这是逻辑回归的关键
  }))
  
  // 编译模型，配置损失函数和优化器
  model.compile({
    loss: tf.losses.logLoss,          // 使用对数损失函数，适合二分类问题
    optimizer: tf.train.adam(0.1)     // 使用Adam优化器，学习率设为0.1
  })
  
  // 准备训练数据：把JavaScript数组转换为TensorFlow张量
  // 输入数据是二维的，每个样本有x和y两个特征
  const inputs = tf.tensor(data.map(p => [p.x, p.y]))
  // 标签数据是一维的，每个样本对应一个标签（0或1）
  const labels = tf.tensor(data.map(p =>p.label))
  
  // 开始训练模型！
  await model.fit(inputs,labels, {
    batchSize: 40,        // 每次训练用40个样本
    epochs: 50,           // 训练50轮
    callbacks: tfvis.show.fitCallbacks(
      {name: '训练过程'},
      ['loss'],           // 只监控损失函数的变化
    )
  })
  
  // 创建一个全局的predict函数，方便用户在浏览器中调用
  // 这个函数接收一个表单对象，从中获取x和y的值
  window.predict = (form) => {
    // 使用模型进行预测
    // 首先把表单中的x和y值转换为张量，注意这里要保持和训练数据相同的格式
    // 这里的-0看起来有点奇怪，可能是为了确保值是数字类型
    const pred = model.predict(tf.tensor([[form.x.value - 0, form.y.value - 0]]))
    
    // 这行很重要！pred是一个Tensor对象，我们需要调用dataSync()方法获取实际的数值
    // dataSync()会返回一个包含张量所有值的TypedArray
    // [0]是因为我们只有一个预测结果，取数组的第一个元素
    const result = pred.dataSync()[0]
    
    // 在控制台打印预测结果，这个结果是一个0到1之间的概率值
    console.log(result)
  };
};